package com.clei.utils;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 线程工具类
 *
 * @author KIyA
 */
public class ThreadUtil {

    /**
     * 核心线程数
     */
    private static final int CORE_POOL_SIZE = 4;

    /**
     * 最大线程数
     */
    private static final int MAX_POOL_SIZE = 9;

    /**
     * 保持活跃时间单位
     */
    private static final TimeUnit TIME_UNIT = TimeUnit.MINUTES;

    /**
     * 保持活跃时间
     */
    private static final int KEEP_ALIVE_TIME = 1;

    /**
     * 队列容量
     */
    private static final int QUEUE_CAPACITY = 20;

    /**
     * 默认工厂名
     */
    private static final String FACTORY_NAME = "custom";

    /**
     * 工厂名map
     */
    private static final ConcurrentHashMap<String, AtomicInteger> FACTORY_MAP = new ConcurrentHashMap<>(4);

    /**
     * 线程池
     *
     * @return ThreadPoolExecutor
     */
    public static ThreadPoolExecutor pool() {
        return pool(QUEUE_CAPACITY);
    }

    /**
     * 线程池
     *
     * @param queueCapacity 队列容量
     * @return ThreadPoolExecutor
     */
    public static ThreadPoolExecutor pool(int queueCapacity) {
        return new ThreadPoolExecutor(
                CORE_POOL_SIZE,
                MAX_POOL_SIZE,
                KEEP_ALIVE_TIME,
                TIME_UNIT,
                new LinkedBlockingQueue<>(queueCapacity),
                factory(FACTORY_NAME),
                new ThreadPoolExecutor.CallerRunsPolicy()
        );
    }

    /**
     * 获取线程工厂
     *
     * @param factoryName 工厂名
     * @return ThreadFactory
     */
    public static ThreadFactory factory(String factoryName) {
        AtomicInteger i = new AtomicInteger(1);
        AtomicInteger old = FACTORY_MAP.putIfAbsent(factoryName, i);
        if (null != old) {
            throw new RuntimeException("工厂名已存在");
        }
        return (r) -> {
            String threadName = factoryName + '_' + i.getAndAdd(1);
            return new Thread(r, threadName);
        };
    }
}
